package com.jinxi.web.dao;

import com.jfinal.plugin.activerecord.Page;
import org.hibernate.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Repository
public class BaseDao {

	@Autowired
	private SessionFactory sessionFactory;

	/**
	 * 保存给定的瞬态实例,首先分配一个生成的标识符。
	 * 
	 * @param t
	 * @return
	 */
	public <T> Serializable save(T t) {
		Session session = sessionFactory.getCurrentSession();
		return session.save(t);
	}

	/**
	 * 
	 * 保存给定的瞬态实例,首先分配一个生成的标识符。
	 * 
	 * @param entityName
	 * @param t
	 * @return
	 */
	public <T> Serializable save(String entityName, T t) {
		Session session = sessionFactory.getCurrentSession();
		return session.save(entityName, t);
	}

	/**
	 * 
	 * 保存(对象)或更新(对象)给定的实例。
	 * 
	 * @param t
	 */
	public <T> void saveOrUpdate(T t) {
		Session session = sessionFactory.getCurrentSession();
		session.saveOrUpdate(t);
	}

	/**
	 * 
	 * 保存(对象)或更新(对象)给定的实例。
	 * 
	 * @param entityName
	 * @param t
	 */
	public <T> void saveOrUpdate(String entityName, T t) {
		Session session = sessionFactory.getCurrentSession();
		session.saveOrUpdate(entityName, t);
	}

	/**
	 * 更新持久实例。
	 * 
	 * @param t
	 */
	public <T> void update(T t) {
		Session session = sessionFactory.getCurrentSession();
		session.update(t);
	}

	/**
	 * 
	 * 更新持久实例。
	 * 
	 * @param entityName
	 * @param t
	 */
	public <T> void update(String entityName, T t) {
		Session session = sessionFactory.getCurrentSession();
		session.update(entityName, t);
	}

	/**
	 * 
	 * 从数据存储中删除一个持久实例。
	 * 
	 * @param t
	 */
	public <T> void delete(T t) {
		Session session = sessionFactory.getCurrentSession();
		session.delete(t);
	}

	/**
	 * 
	 * 从数据存储中删除一个持久实例。
	 * 
	 * @param entityName
	 * @param t
	 */
	public <T> void delete(String entityName, T t) {
		Session session = sessionFactory.getCurrentSession();
		session.delete(entityName, t);
	}

	/**
	 * 
	 * Copy the state of the given object onto the persistent object with the
	 * same identifier.
	 * 
	 * @param t
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T merge(T t) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.merge(t);
	}

	/**
	 * 
	 * Copy the state of the given object onto the persistent object with the
	 * same identifier.
	 * 
	 * @param entityName
	 * @param t
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T merge(String entityName, T t) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.merge(entityName, t);
	}

	/**
	 * 
	 * Make a transient instance persistent.
	 * 
	 * @param t
	 */
	public <T> void persist(T t) {
		Session session = sessionFactory.getCurrentSession();
		session.persist(t);
	}

	/**
	 * 
	 * Make a transient instance persistent.
	 * 
	 * @param entityName
	 * @param t
	 */
	public <T> void persist(String entityName, T t) {
		Session session = sessionFactory.getCurrentSession();
		session.persist(entityName, t);
	}

	/**
	 * 
	 * Re-read the state of the given instance from the underlying database.
	 * 
	 * @param t
	 */
	public <T> void refresh(T t) {
		Session session = sessionFactory.getCurrentSession();
		session.refresh(t);
	}

	/**
	 * 
	 * Re-read the state of the given instance from the underlying database,
	 * with the given LockMode.
	 * 
	 * @param t
	 * @param lockOptions
	 */
	public <T> void refresh(T t, LockOptions lockOptions) {
		Session session = sessionFactory.getCurrentSession();
		session.refresh(t, lockOptions);
	}

	/**
	 * 
	 * Re-read the state of the given instance from the underlying database.
	 * 
	 * @param entityName
	 * @param t
	 */
	public <T> void refresh(String entityName, T t) {
		Session session = sessionFactory.getCurrentSession();
		session.refresh(entityName, t);
	}

	/**
	 * 
	 * Re-read the state of the given instance from the underlying database,
	 * with the given LockMode.
	 * 
	 * @param entityName
	 * @param t
	 * @param lockOptions
	 */
	public <T> void refresh(String entityName, T t, LockOptions lockOptions) {
		Session session = sessionFactory.getCurrentSession();
		session.refresh(entityName, t, lockOptions);
	}

	/**
	 * 
	 * Persist the state of the given detached instance, reusing the current
	 * identifier value.
	 * 
	 * @param t
	 * @param replicationMode
	 */
	public <T> void replicate(T t, ReplicationMode replicationMode) {
		Session session = sessionFactory.getCurrentSession();
		session.replicate(t, replicationMode);
	}

	/**
	 * 
	 * Persist the state of the given detached instance, reusing the current
	 * identifier value.
	 * 
	 * @param entityName
	 * @param t
	 * @param replicationMode
	 */
	public <T> void replicate(String entityName, T t, ReplicationMode replicationMode) {
		Session session = sessionFactory.getCurrentSession();
		session.replicate(entityName, t, replicationMode);
	}

	/**
	 * 
	 * Return the persistent instance of the given entity class with the given
	 * identifier, or null if there is no such persistent instance.
	 * 
	 * @param clazz
	 * @param id
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T get(Class<T> clazz, Serializable id) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.get(clazz, id);
	}

	/**
	 * 
	 * Return the persistent instance of the given entity class with the given
	 * identifier, or null if there is no such persistent instance.
	 * 
	 * @param clazz
	 * @param id
	 * @param lockOptions
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T get(Class<T> clazz, Serializable id, LockOptions lockOptions) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.get(clazz, id, lockOptions);
	}

	/**
	 * 
	 * Return the persistent instance of the given entity class with the given
	 * identifier, or null if there is no such persistent instance.
	 * 
	 * @param entityName
	 * @param id
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T get(String entityName, Serializable id) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.get(entityName, id);
	}

	/**
	 * 
	 * Return the persistent instance of the given entity class with the given
	 * identifier, or null if there is no such persistent instance.
	 * 
	 * @param entityName
	 * @param id
	 * @param lockOptions
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T get(String entityName, Serializable id, LockOptions lockOptions) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.get(entityName, id, lockOptions);
	}

	/**
	 * 
	 * Return the persistent instance of the given entity class with the given
	 * identifier, assuming that the instance exists.
	 * 
	 * @param clazz
	 * @param id
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T load(Class<T> clazz, Serializable id) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.load(clazz, id);
	}

	/**
	 * 
	 * Return the persistent instance of the given entity class with the given
	 * identifier, assuming that the instance exists.
	 * 
	 * @param clazz
	 * @param id
	 * @param lockOptions
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T load(Class<T> clazz, Serializable id, LockOptions lockOptions) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.load(clazz, id, lockOptions);
	}

	/**
	 * 
	 * Return the persistent instance of the given entity class with the given
	 * identifier, assuming that the instance exists.
	 * 
	 * @param entityName
	 * @param id
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T load(String entityName, Serializable id) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.load(entityName, id);
	}

	/**
	 * 
	 * Return the persistent instance of the given entity class with the given
	 * identifier, assuming that the instance exists.
	 * 
	 * @param entityName
	 * @param id
	 * @param lockOptions
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T load(String entityName, Serializable id, LockOptions lockOptions) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.load(entityName, id, lockOptions);
	}

	/**
	 * 
	 * 根据Hql查询一个持久化实例
	 * 
	 * @param hql
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T queryObjectByHql(String hql) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		query.setMaxResults(1);
		return (T) query.uniqueResult();
	}

	/**
	 * 
	 * 根据Hql查询一个持久化实例， 占位符查询
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T queryObjectByHql(String hql, Object... args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		setParams(query, args);
		query.setMaxResults(1);
		return (T) query.uniqueResult();
	}

	/**
	 * 根据Hql查询一个持久化实例， 命名参数查询
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T queryObjectByHql(String hql, Map<String, Object> args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		setParams(query, args);
		query.setMaxResults(1);
		return (T) query.uniqueResult();
	}

	/**
	 * 根据Hql查询一个持久化实例， 命名参数查询 分页
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryObjectByHql(String hql, Map<String, Object> args, Integer offset, Integer pageSize) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		setParams(query, args);
		query.setFirstResult(offset);
		query.setMaxResults(pageSize);
		return query.list();
	}

	/**
	 * 
	 * 根据SQL查询一个持久化实例
	 * 
	 * @param sql
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T queryObjectBySql(String sql) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		query.setMaxResults(1);
		return (T) query.uniqueResult();
	}

	/**
	 * 
	 * 根据Hql查询一个持久化实例， 占位符查询
	 * 
	 * @param sql
	 * @param args
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T queryObjectBySql(String sql, Object... args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		setParams(query, args);
		query.setMaxResults(1);
		return (T) query.uniqueResult();
	}

	/**
	 * 
	 * 根据Hql查询一个持久化实例， 命名参数查询
	 * 
	 * @param sql
	 * @param args
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T queryObjectBySql(String sql, Map<String, Object> args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		setParams(query, args);
		query.setMaxResults(1);
		return (T) query.uniqueResult();
	}

	/**
	 * 
	 * @param hql
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryByHql(String hql) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		return query.list();
	}

	/**
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryByHql(String hql, Object... args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		setParams(query, args);
		return query.list();
	}

	/**
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryByHql(String hql, Map<String, Object> args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		setParams(query, args);
		return query.list();
	}

	/**
	 * 
	 * @param sql
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryBySql(String sql) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		return query.list();
	}

	/**
	 * 
	 * @param sql
	 * @param args
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryBySql(String sql, Object... args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		setParams(query, args);
		return query.list();
	}

	/**
	 * 
	 * @param sql
	 * @param args
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryBySql(String sql, Map<String, Object> args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		setParams(query, args);
		return query.list();
	}

	/**
	 * 
	 * @param hql
	 * @return
	 */
	public long queryCountByHql(String hql) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		return (long) query.uniqueResult();
	}

	/**
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	public long queryCountByHql(String hql, Object... args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		setParams(query, args);
		return (long) query.uniqueResult();
	}

	/**
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	public long queryCountByHql(String hql, Map<String, Object> args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		setParams(query, args);
		return (long) query.uniqueResult();
	}

	/**
	 * 
	 * @param hql
	 * @return
	 */
	public long queryCountBySql(String sql) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		return (long) query.uniqueResult();
	}

	/**
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	public long queryCountBySql(String sql, Object... args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		setParams(query, args);
		return (long) query.uniqueResult();
	}

	/**
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	public long queryCountBySql(String sql, Map<String, Object> args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		setParams(query, args);
		return (long) query.uniqueResult();
	}

	/**
	 * 
	 * @param hql
	 * @return
	 */
	public int updateByHql(String hql) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		return query.executeUpdate();
	}

	/**
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	public int updateByHql(String hql, Object... args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		setParams(query, args);
		return query.executeUpdate();
	}

	/**
	 * 
	 * @param hql
	 * @param args
	 * @return
	 */
	public int updateByHql(String hql, Map<String, Object> args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(hql);
		setParams(query, args);
		return query.executeUpdate();
	}

	/**
	 * 
	 * @param sql
	 * @return
	 */
	public int updateBySql(String sql) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		return query.executeUpdate();
	}

	/**
	 * 
	 * @param sql
	 * @param args
	 * @return
	 */
	public int updateBySql(String sql, Object... args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		setParams(query, args);
		return query.executeUpdate();
	}

	/**
	 * 
	 * @param sql
	 * @param args
	 * @return
	 */
	public int updateBySql(String sql, Map<String, Object> args) {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createSQLQuery(sql);
		setParams(query, args);
		return query.executeUpdate();
	}

	@SuppressWarnings("unchecked")
	public <T> Page<T> queryForPageByHql(String hql, int page, int length, Object... args) {
		Session session = sessionFactory.getCurrentSession();

		Query query = session.createQuery(hql);

		setParams(query, args);

		ScrollableResults scroll = query.scroll();
		scroll.last();

		int totalRow = scroll.getRowNumber() == -1 ? 0 : scroll.getRowNumber() + 1;
		int totalPage = totalRow % length > 0 ? totalRow / length + 1 : totalRow / length;

		query.setFirstResult((page - 1) * length);
		query.setMaxResults(length);

		return new Page<T>(query.list(), page, length, totalPage, totalRow);
	}
	
	@SuppressWarnings("unchecked")
	public <T> Page<T> queryForPageByHql(String hql, int page, int length, Map<String, Object> args) {
		Session session = sessionFactory.getCurrentSession();

		Query query = session.createQuery(hql);

		setParams(query, args);

		ScrollableResults scroll = query.scroll();
		scroll.last();

		int totalRow = scroll.getRowNumber() == -1 ? 0 : scroll.getRowNumber() + 1;
		int totalPage = totalRow % length > 0 ? totalRow / length + 1 : totalRow / length;

		query.setFirstResult((page - 1) * length);
		query.setMaxResults(length);

		return new Page<T>(query.list(), page, length, totalPage, totalRow);
	}

	@SuppressWarnings("unchecked")
	public <T> Page<T> queryForPageBySql(String sql, int page, int length, Object... args) {
		Session session = sessionFactory.getCurrentSession();

		Query query = session.createSQLQuery(sql);

		setParams(query, args);

		ScrollableResults scroll = query.scroll();
		scroll.last();

		int totalRow = scroll.getRowNumber() == -1 ? 0 : scroll.getRowNumber() + 1;
		int totalPage = totalRow % length > 0 ? totalRow / length + 1 : totalRow / length;

		query.setFirstResult((page - 1) * length);
		query.setMaxResults(length);

		return new Page<T>(query.list(), page, length, totalPage, totalRow);
	}
	

	@SuppressWarnings("unchecked")
	public <T> Page<T> queryForPageBySql(String sql, int page, int length, Map<String, Object> args) {
		Session session = sessionFactory.getCurrentSession();

		Query query = session.createSQLQuery(sql);

		setParams(query, args);

		ScrollableResults scroll = query.scroll();
		scroll.last();

		int totalRow = scroll.getRowNumber() == -1 ? 0 : scroll.getRowNumber() + 1;
		int totalPage = totalRow % length > 0 ? totalRow / length + 1 : totalRow / length;

		query.setFirstResult((page - 1) * length);
		query.setMaxResults(length);

		return new Page<T>(query.list(), page, length, totalPage, totalRow);
	}

	/**
	 * 
	 * @param query
	 * @param args
	 */
	private void setParams(Query query, Map<String, Object> args) {
		if (null == query)
			return;
		if (null == args || args.isEmpty())
			return;
		Set<String> set = args.keySet();
		for (String string : set) {
			Object object = args.get(string);
			if (object instanceof List) {
				List list = (List) object;
				query.setParameterList(string, list);
				continue;
			}
			query.setParameter(string, args.get(string));
		}
	}

	/**
	 * 
	 * @param query
	 * @param args
	 */
	private void setParams(Query query, Object... args) {
		if (null == query)
			return;
		if (null == args)
			return;
		int i = 0;
		for (Object object : args) {
			query.setParameter(i++, object);
		}
	}

}
